package manager

import (
	"strings"

	"github.com/Kindling-project/chaos-backend/api"
	"github.com/Kindling-project/chaos-backend/tools/kubernetescli"
	"github.com/hashicorp/go-multierror"
)

var DefaultManager ChaosManager

func init() {
	DefaultManager = ChaosManager{
		Clis: make(map[string]kubernetescli.ChaosCli),
	}
	DefaultManager.Clis["networkchaos"] = &kubernetescli.NetworkChaosCli
	DefaultManager.Clis["stresschaos"] = &kubernetescli.StressChaosCli
	DefaultManager.Clis["jvmchaos"] = &kubernetescli.JVMChaosCli
	DefaultManager.Clis["iochaos"] = &kubernetescli.IOChaosCli
	DefaultManager.Clis["httpchaos"] = &kubernetescli.HTTPChaosCli
}

type ChaosManager struct {
	Clis map[string]kubernetescli.ChaosCli
}

func (cm *ChaosManager) ListChaos() ([]api.Chaos, error) {
	var result = make([]api.Chaos, 0)
	var mutiErr *multierror.Error
	for _, cli := range cm.Clis {
		if list, err := cli.List(); err == nil {
			// list
			chaos := cli.ToChaosVO(&list)
			result = append(result, chaos...)
		} else {
			multierror.Append(mutiErr, err)
		}
	}
	return result, mutiErr.ErrorOrNil()
}

func (cm *ChaosManager) CreateChaos(vo *api.Chaos) (*api.Chaos, error) {
	// ToChaos used to accept api.Chaos from front, and changed into real chaos resources
	var chaos_type, action string
	if vo.ComplexAction != "" {
		typeAndAction := strings.Split(vo.ComplexAction, "#")
		if len(typeAndAction) > 1 {
			chaos_type = typeAndAction[0]
			action = typeAndAction[1]
			vo.Params = append(vo.Params, api.ChaosParams{Name: "action", Value: action})
		}
		if len(typeAndAction) > 2 {
			vo.Params = append(vo.Params, api.ChaosParams{Name: "option", Value: typeAndAction[2]})
		}
	} else {
		chaos_type = vo.ChaosType
	}

	cli := cm.Clis[chaos_type]
	if obj, err := cli.Create(vo); err != nil {
		return nil, err
	} else {
		return &cli.ToChaosVO(&obj)[0], nil
	}
}

func (cm *ChaosManager) DeleteChaos() (api.Chaos, error) {
	var lastChaos api.Chaos
	var mutiErr *multierror.Error
	for _, cli := range cm.Clis {
		if chaosDeleted, err := cli.DeleteAll(); err != nil {
			multierror.Append(mutiErr, err)
		} else {
			if chaosInfo := (cli.ToChaosVO(&chaosDeleted)); len(chaosInfo) > 0 {
				lastChaos = chaosInfo[0]
			}
		}
	}
	return lastChaos, mutiErr.ErrorOrNil()
}

func (cm *ChaosManager) Status() (*api.Chaos, error) {
	var lastChaos *api.Chaos
	var mutiErr *multierror.Error
	for _, cli := range cm.Clis {
		if chaos, err := cli.List(); err != nil {
			multierror.Append(mutiErr, err)
		} else {
			for _, v := range cli.ToChaosVO(&chaos) {
				// TODO filter pause
				lastChaos = &v
			}
		}
	}

	return lastChaos, mutiErr.ErrorOrNil()
}

func ListChaos() ([]api.Chaos, error) {
	return DefaultManager.ListChaos()
}

func CreateChaos(vo *api.Chaos) (*api.Chaos, error) {
	return DefaultManager.CreateChaos(vo)
}

func DeleteChaos() (api.Chaos, error) {
	return DefaultManager.DeleteChaos()
}

func Status() (*api.Chaos, error) {
	return DefaultManager.Status()
}
